#pragma once
#include <unistd.h>
#include <pthread.h>
#include <iostream>
#include "Thread.hpp"
#include "RingQueue.hpp"
#include <vector>
#include <string>
template<class T>
class ThreadPool
{
    ThreadPool(int n):_rq(100)
    {
        for(int i = 0; i < n; i++)
        {
            ThreadData td("thread" + std::to_string(i));
            _threads.emplace_back(td, std::bind(&ThreadPool<T>::ThreadRun, this, std::placeholders::_1));
        }
    }
public:
    ThreadPool(const ThreadPool& tmp) = delete;
    ThreadPool& operator=(const ThreadPool& tmp) = delete;

    static ThreadPool<T>* GetInstance()
    {
        if(_p == nullptr)
        {
            LockGuard lock(&mtx);
            if(_p == nullptr)
            {
                _p = new ThreadPool<T>(10);
            }
        }
        return _p;
    }


    void ThreadRun(ThreadData& td)
    {
        while (1)
        {
            T t;
            _rq.Pop(&t);
            t();
            std::cout << pthread_self() << ": " << t.GetResult() << "\n";
        }
    }

    void Start()
    {
        for(auto& thread : _threads)
        {
            thread.Start();
        }
    }

    void push(const T& x)
    {
        _rq.Push(x);
    }

    ~ThreadPool()
    {
        for(auto& thread: _threads)
        {
            thread.Join();
        }
    }
private:
    std::vector<Thread<ThreadData>> _threads;
    RingQueue<T> _rq;
    static ThreadPool * _p;
    static pthread_mutex_t mtx;
};
template<class T>
ThreadPool<T>* ThreadPool<T>::_p = nullptr;
template<class T>
pthread_mutex_t ThreadPool<T>::mtx = PTHREAD_MUTEX_INITIALIZER;